<!DOCTYPE html>
<html>

    <head>
		<meta charset="utf-8" />
		<title>FlipIt</title>
	</head>
	<style>
		* {
			margin: 0;
			padding: 0;
		}
		ul {
			list-style: none;
		}
		li {
			opacity: 0;
			transition: all .5s ease 0s;
		}
		li.show {
			opacity: 1;
		}
		li {
			padding: 2px;
			margin-right: 2px;
			border-bottom: 1px dashed #AAA;
		}
		li span.checked {
			background-color: #999;
		}
		.panel {
			width: 200px;
			float: left;
		}
		.body {
			border: 1px solid #AAA;
			padding: 6px;
			padding-left: 0;
		}
		.row {
			width: 100%;
			padding: 4px;
		}
		.row a {
			margin-right: 6px;
			color: #369;
			text-decoration: none;
		}
		input {
			width: 32px;
		}
		.row a:hover {
			color: #136;
			text-decoration: underline;
		}
		a.hide {
			display: none;
		}
		a.show {
			display: inline-block;
		}
		#solver span {
			display: inline-block;
			border: 1px solid #AAA;
			width: 1em;
			height: 1em;
			margin-right: 4px;
			line-height: 2em;
		}
		#canvas {
			float: right;
			font-size: 0;
			-webkit-text-size-adjust: none;
			padding: 4px;
			border-radius: 4px;
			background-color: #ACC8FA;
		}
		#canvas div {
			display: inline-block;
			border-radius: 3px;
			cursor: pointer;
			margin: 1px;
		}
		.border1 {
			border: 2px solid #D00 !important;
			transition: all .5s;
		}
		.border2 {
			border: 2px solid #0D0 !important;
			transition: all .5s ease 0s;
		}
		.border3 {
			border: 2px solid #D0D !important;
			transition: all .5s ease 0s;
		}
		.tile1 {
			border: 2px solid #444;
			background-color: rgb(230, 171, 94);
			animation: flip1 .8s ease;
		}
		.tileEnd1 {
			border: 2px solid #444;
			background-color: rgb(107, 155, 244);
		}
		@keyframes flip1 {
			1% {
				background-color: rgb(230, 171, 94)
			}
			49% {
				transform: rotate3d(-1, 1, 0, -90deg);
				background-color: rgb(230, 171, 94)
			}
			50% {
				transform: rotate3d(-1, 1, 0, -90deg);
				background-color: rgb(107, 155, 244)
			}
			100% {
				transform: rotate3d(-1, 1, 0, -180deg);
				background-color: rgb(107, 155, 244);
			}
		}
		.tileEnd1:hover {
			border: 2px solid #888;
			background-color: rgb(127, 175, 254);
			transition: all .5s ease 0s;
		}
		.tile2 {
			border: 2px solid #444;
			background-color: rgb(107, 155, 244);
			animation: flip2 .8s ease;
		}
		.tileEnd2 {
			border: 2px solid #444;
			background-color: rgb(230, 171, 94);
		}
		@keyframes flip2 {
			1% {
				background-color: rgb(107, 155, 244)
			}
			49% {
				transform: rotate3d(-1, 1, 0, -90deg);
				background-color: rgb(107, 155, 244)
			}
			50% {
				transform: rotate3d(-1, 1, 0, -90deg);
				background-color: rgb(230, 171, 94)
			}
			100% {
				transform: rotate3d(-1, 1, 0, -180deg);
				background-color: rgb(230, 171, 94);
			}
		}
		.tile2:hover {
			border: 2px solid #888;
			background-color: rgb(250, 175, 114);
			transition: all .5s ease 0s;
		}
	</style>

	<body>
		<div id="main" style="margin: 10px auto;">
			<div class="panel">
				<div class="title">
					<h1>翻纸片</h1>
				</div>
				<div class="body">
					<div class="row">共计：<span id="stepCounter">0</span>步</div>
					<div class="row" id="controlBar">
						每行放
						<input type="text" id="n" />个
						<br/> 随机翻
						<input type="text" id="r" />个
						<br/>
						<a href="javascript:play()">开始</a>
					</div>
					<div class="row">
						<a class="calAnswer hide" href="javascript:showAnswer()">
						还是告诉我答案吧~</a>
						<a class="showAnswer hide" href="javascript:showAnswer()">
						再看看</a>
					</div>
					<ul class="row" id="solver"></ul>
				</div>
			</div>
			<div id="canvas">
			</div>
		</div>
	</body>
	<script type="text/javascript">
		 //query selector
		$ = function(id) {
			if (typeof id != "string") return;
			if (id.indexOf("#") == 0) {
				return document.getElementById(id.substring(1));
			}
			if (id.indexOf(".") == 0) {
				return document.getElementsByClassName(id.substring(1));
			}
			return document.getElementsByTagName(id);
		}
		var addClass = function(e, c) {
			if (hasClass(e, c)) return;
			e.className += " " + c;
			return e;
		}
		var hasClass = function(e, c) {
			return e && (new RegExp("\\b" + c + "\\b")).test(e.className);
		}
		var removeClass = function(e, c) {
				if (e && e.className) {
					e.className = e.className.replace(new RegExp("\\b" + c + "\\b", "g"), "").replace(/\s+/g, " ");
				}
				return e;
			}
			//取count个不重复的随机整数，范围是1~n
			//有count参数时，返回count个随机数，否则返回一个
		var random = function(n, count) {
				if (count && n > count) {
					var t = {},
						ret = [];
					while (1) {
						var r = random(n);
						if (!t[r]) {
							ret.push(r);
							t[r] = true;
							if (ret.length == count) break;
						}
					}
					return ret.sort();
				}
				return Math.round(Math.random() * (n - 1));
			}
			//设置页面布局
		var setSize = function(minHeight) {
			minHeight -= 30;
			$("#main").style.width = +minHeight + 220 + "px";
			$("#canvas").style.width = +minHeight + "px";
		}
		setSize(window.innerHeight);
		 //添加一列答案
		var newResult = function(r, n) {
				var s = "";
				for (var i = 0; i < n; i++) {
					s += r[i] ? "<span class='checked'></span>" : "<span></span>";
				}
				$("#solver").innerHTML += "<li>" + s + "</li>";
			}
			//展示答案，如果未计算过则计算，否则直接显示
		var showAnswer = (function() {
			var timer = "";
			var ranNum = random(5) + 1;;
			//闭包，防止重复点击
			return function() {
				//增加查看答案的难度，猜一个随机数
				if (window.prompt("猜一个1-5的数字") != ranNum) {
					return;
				}
				ranNum = random(5) + 1;
				//防止重复点击
				if (timer) return;
				removeClass($(".calAnswer")[0], "show");
				addClass($(".calAnswer")[0], "hide");
				//是否已经计算答案
				if (!$("#solver").innerHTML) {
					var tiles = $(".tile"),
						changed = [];
					//获取反转了的纸片
					for (var i = 0; i < tiles.length; i++) {
						if (hasClass(tiles[i], "tile2")) {
							changed.push(i);
						}
					}
					$("#solver").innerHTML = "";
					//计算答案
					solve(changed, Math.sqrt(tiles.length));
				}
				var lis = $("li"),
					i = 0;
				if (!lis || !lis.length) {
					$("#solver").innerHTML = "答案就是——没有答案~";
					return;
				}
				removeClass($(".showAnswer")[0], "hide");
				addClass($(".showAnswer")[0], "show");
				//逐行显示答案
				var show = function() {
					addClass(lis[i], "show");
					timer = setTimeout(function() {
						removeClass(lis[i], "show");
						if (++i < lis.length) {
							show();
						} else {
							timer = "";
						}
					}, 1000);
				}
				show();
			}
		})();
		 //成功提示
		var done = function() {
				alert("哇~~~\n你竟然成功了！");
			}
			//初始化游戏
		var play = function() {
				//每行个数
				var n = $("#n").value;
				if (!n || n < 3) {
					alert("请输入一个正常的个数（>2）!")
					return;
				} else if (n > 12) {
					if (!window.confirm("这么大的数字？会卡的~~")) return;
				}
				//随机数量
				var r = $("#r").value;
				if (!r || r < 1 || r >= n * n) {
					alert("请输入一个正常的随机个数（>=1 且<=" + n * n + "）!")
					return;
				}
				setSize(window.innerHeight);
				$("#canvas").innerHTML = "";
				$("#solver").innerHTML = "";
				$("#stepCounter").innerHTML = "0";
				var canvas = init(n);
				r = random(n * n, r);
				console.log(r);
				showRandom(r, canvas);
				//注册点击事件
				canvas.onclick = function() {
					var e = arguments[0].target;
					var all = getAllTile(this);
					for (var t in all) {
						if (all[t] == e) {
							flip(this, n, t);
							//步数统计
							$("#stepCounter").innerHTML = ++$("#stepCounter").innerHTML;
							//检查是否完成
							var check = true;
							for (var c in all) {
								if (hasClass(all[c], "tile2")) {
									check = false;
									break;
								}
							}
							if (check) {
								done();
							}
						}
					}
				};
				//动画结束事件监听
				canvas.addEventListener("animationend", function() {
					var e = arguments[0].target;
					e.className = e.className.replace(/tileEnd\d/, "")
						.replace('tile1', 'tile1 tileEnd1')
						.replace('tile2', 'tile2 tileEnd2');
				}, false);
				//solve(r, n);
				removeClass($(".showAnswer")[0], "show");
				addClass($(".showAnswer")[0], "hide");
				removeClass($(".calAnswer")[0], "hide");
				addClass($(".calAnswer")[0], "show");
			}
			//获取所有纸片
		var getAllTile = function(o) {
				return o.getElementsByTagName("div");
			}
			//获取第i个纸片
		var getTile = function(o, i) {
				return getAllTile(o)[i];
			}
			//初始化，n：每行个数，r随机个数
		var init = function(n, r) {
				var canvas = $("#canvas");
				var width = canvas.style.width;
				width = width.substr(0, width.length - 2);
				width = Math.floor(width / n - 6);
				canvas.style.width = (width + 6) * n + "px";
				var e = document.createElement("div");
				addClass(e, "tile")
				addClass(e, "tile1")
				e.style.height = width + "px";
				e.style.width = width + "px";
				for (var i = n * n; i > 0; i--) {
					canvas.appendChild(e.cloneNode());
				}
				return canvas;
			}
			//获取4临域坐标（-1为无效）
		var coords = function(n, i) {
				n = +n;
				i = +i;
				var b = i - i % n, //该行第一个的下标
					c = b + n, //该行最后一个的下标
					u = i - n, //上
					d = i + n, //下
					l = i - 1, //左
					r = i + 1; //右
				u = u >= 0 ? u : -1;
				d = d < n * n ? d : -1;
				l = l >= b ? l : -1;
				r = r < c ? r : -1;
				return [u, d, l, r];
			}
			//模拟触发反转
		var hit = function(a, n, i) {
				var m = coords(n, i)
				a[m[0]] = !a[m[0]];
				a[m[1]] = !a[m[1]];
				a[m[2]] = !a[m[2]];
				a[m[3]] = !a[m[3]];
				a[i] = !a[i];
				return a;
			}
			//处理反转纸片的效果
		var toggle = function(e) {
				if (!e) return;
				if (hasClass(e, "tile1")) {
					removeClass(e, "tile1");
					addClass(e, "tile2");
				} else {
					removeClass(e, "tile2");
					addClass(e, "tile1");
				}
			}
			//反转纸片
		var flip = function(canvas, n, i) {
				var all = getAllTile(canvas);
				var m = coords(n, i);
				toggle(all[m[0]]);
				toggle(all[m[1]]);
				toggle(all[m[2]]);
				toggle(all[m[3]]);
				toggle(all[i]);
				return all;
			}
			//模拟n位二进制a的加一运算
		var plus = function(a, n) {
				for (var i = 0; i < n; i++) {
					a[i] = !a[i];
					if (a[i]) break;
				}
				//溢出
				if (i == n) return false;
				return a;
			}
			//检查是否全位0
		var check = function(a) {
				for (var i = 0; i < a.length; i++) {
					if (a[i]) return true;
				}
				return false;
			}
			//显示随机反转的纸片
		var showRandom = function(r, c) {
				for (var ran in r) {
					removeClass(getTile(c, r[ran]), "tile1");
					addClass(getTile(c, r[ran]), "tile2");
				}
			}
			//计算答案
		var solve = function(r, n) {
			var a = [], //全部纸片
				counter = [], //累加器
				l = n * n - 1; //纸片个数
			//初始化纸片
			for (var i = l; i >= 0; i--) {
				a[i] = false;
			}
			//设置初始随机纸片
			for (var ran in r) {
				a[r[ran]] = true;
			}
			//尝试函数
			var fn = function() {
					var temp = [];
					//复制全部纸皮的数组
					for (var i in a) {
						temp[i] = a[i];
					}
					//累加并判断是否溢出
					if (plus(counter, n)) {
						//按累加器点击第一行
						for (var i = 0; i < n; i++) {
							if (counter[i]) hit(temp, n, i);
						}
						//按上一行逐个点击
						for (var i = n; i <= l; i++) {
							if (temp[i - n]) {
								hit(temp, n, i);
							}
						}
						//检查最终结果
						if (!check(temp)) {
							//显示答案
							newResult(counter, n);
						}
						return true;
					} else {
						//溢出，结束标记
						return false;
					}
				}
				//耗时总和
			var count = 0;
			//计时器
			var timer = new Date();
			while (true) {
				//计时及超时提醒
				if (new Date() - timer > 5000) {
					count += (new Date() - timer);
					if (!confirm(count + "ms, continue?")) {
						break;
					}
					timer = new Date();
				}
				//逐次调用尝试函数
				if (!fn())
					break;
			}
		}
	</script>

</html>